/*
    Project:        Home Power Control
    File:           HPCMCP3008.cpp
    Author:         christian@karunamusic.at
    Date:           11.2014
    Description:    Class to access and query the A/D converter-chip MCP3008 via SPI.
 */

#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdexcept>
#include <linux/spi/spidev.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>
#include "globals.h"
#include "HPCMCP3008.h"

using namespace std;

pthread_mutex_t HPCMCP3008::m_Lock = PTHREAD_MUTEX_INITIALIZER;

HPCMCP3008::HPCMCP3008()
{
    PROFILE_THIS;
    this->m_Mode = SPI_MODE_0;
    this->m_BitsPerWord = 8;
    this->m_Speed = 1000000;
    this->m_spifd = -1;

    this->spiOpen(std::string("/dev/spidev0.0"));
}

HPCMCP3008::HPCMCP3008( std::string devspi, unsigned char spiMode,
                        unsigned int spiSpeed, unsigned char spiBitsPerWord)
{
    PROFILE_THIS;
    this->m_Mode = spiMode;
    this->m_BitsPerWord = spiBitsPerWord;
    this->m_Speed = spiSpeed;
    this->m_spifd = -1;

    this->spiOpen(devspi);
}

HPCMCP3008::~HPCMCP3008()
{
    PROFILE_THIS;
    this->spiClose();
}

int HPCMCP3008::spiOpen( std::string devspi )
{
    PROFILE_THIS;
    int ret = -1;
    lock();
    this->m_spifd = open( devspi.c_str(),O_RDWR );
    if ( this->m_spifd < 0 )
    {
        unlock();
        THROW( "could not open SPI device" );
    }

    ret = ioctl( this->m_spifd,SPI_IOC_WR_MODE,&(this->m_Mode) );
    if ( ret < 0 )
    {
        unlock();
        THROW( "could not set SPIMode (WR)...ioctl failed" );
    }

    ret = ioctl( this->m_spifd,SPI_IOC_RD_MODE,&(this->m_Mode) );
    if ( ret < 0 )
    {
        unlock();
        THROW( "could not set SPIMode (RD)...ioctl failed" );
    }

    ret = ioctl( this->m_spifd,SPI_IOC_WR_BITS_PER_WORD,&(this->m_BitsPerWord) );
    if ( ret < 0 )
    {
        unlock();
        THROW( "could not set SPI BitsPerWord (WR)...ioctl failed" );
    }

    ret = ioctl( this->m_spifd,SPI_IOC_RD_BITS_PER_WORD,&(this->m_BitsPerWord) );
    if ( ret < 0 )
    {
        unlock();
        THROW( "could not set SPI BitsPerWord (RD)...ioctl failed" );
    }

    ret = ioctl( this->m_spifd,SPI_IOC_WR_MAX_SPEED_HZ,&(this->m_Speed) );
    if ( ret < 0 )
    {
        unlock();
        THROW( "could not set SPI Speed (WR)...ioctl failed" );
    }

    ret = ioctl( this->m_spifd,SPI_IOC_RD_MAX_SPEED_HZ,&(this->m_Speed) );
    if ( ret < 0 )
    {
        unlock();
        THROW( "could not set SPI Speed (RD)...ioctl failed" );
    }
    unlock();

    return ret;
}

int HPCMCP3008::spiClose()
{
    PROFILE_THIS;
    int ret = -1;

    lock();
    ret = close( this->m_spifd );
    unlock();
    if(ret < 0)
    {
        THROW( "could not close SPI device" );
    }
    return ret;
}

int HPCMCP3008::spiWriteRead( unsigned char *data, int length )
{
    PROFILE_THIS;
    struct spi_ioc_transfer spi[length];
    int i = 0;
    int ret = -1;

    for ( i = 0; i < length; i++ )
    {
        spi[i].tx_buf = (unsigned long)(data + i);
        spi[i].rx_buf = (unsigned long)(data + i);
        spi[i].len = sizeof(*(data + i));

        spi[i].delay_usecs = 0;
        spi[i].speed_hz = this->m_Speed;
        spi[i].bits_per_word = this->m_BitsPerWord;
        spi[i].cs_change = 0;
    }

    lock();
    ret = ioctl( this->m_spifd,SPI_IOC_MESSAGE(length),&spi );
    unlock();
    if ( ret < 0 )
    {
        THROW( "problem transmitting spi data..ioctl failed" );
    }

    return ret;
}

void HPCMCP3008::lock()
{
    pthread_mutex_lock(&m_Lock);
}

void HPCMCP3008::unlock()
{
    pthread_mutex_unlock(&m_Lock);
}

